package eu.telecombretagne.rsm.base.event.manager;

import java.util.HashMap;

import eu.telecombretagne.rsm.base.event.AckEvent;
import eu.telecombretagne.rsm.base.event.AlarmInitEvent;
import eu.telecombretagne.rsm.base.event.AppMessageEvent;
import eu.telecombretagne.rsm.base.event.AppMessageReturnEvent;
import eu.telecombretagne.rsm.base.event.Event;
import eu.telecombretagne.rsm.base.event.FrameEvent;
import eu.telecombretagne.rsm.base.event.TimerExpiredEvent;
import eu.telecombretagne.rsm.gui.NameConsts;
import eu.telecombretagne.rsm.gui.frames.DesktopFrame;

public class EventParamsManager {	
	private static HashMap<Class<? extends Event>, EventParameters> eventParametersMap = new HashMap<Class<? extends Event>, EventParameters>();
	private static Boolean useSimplifiedValues = false;
	
	/**
	 * Sets the use of the simplified settings.
	 * @param pUseSimplifiedValues If set to <i>true</i> simplified settings will be used.
	 */
	public static void setUseSimplifiedValues(Boolean pUseSimplifiedValues){
		useSimplifiedValues = pUseSimplifiedValues;
	}
	
	/**
	 * Checks whether the simplified settings are enabled.
	 * @return Returns <i>true</i> if the simplified settings are enabled.
	 */
	public static Boolean isUseSimplifiedValuesEnabled(){
		return useSimplifiedValues;
	}
	
	/**
	 * Sets a global probability value.
	 * 
	 * @param pProbability A probability value.
	 * @return Returns a value of a probability of the type Double which has been set.
	 */
	public static Double setGlobalEventProbability(Double pProbability){
		return setEventProbability(Event.class, pProbability);
	}
	
	/**
	 * Sets a global delay value.
	 * 
	 * @param pDelay A delay value.
	 * @return Returns a value of a delay of the type Long which has been set.
	 */
	public static Long setGlobalEventDelay(Long pDelay){
		return setEventDelay(Event.class, pDelay);
	}
	
	/**
	 * Set the global event effects.
	 * 
	 * @param pEffects If set to <i>true</i> effects are enabled.
	 * @param pDuplication If set to <i>true</i> an event duplication is enabled. If set to <i>false</i> an event loss is enabled.
	 */
	public static void setGlobalEventEffects(Boolean pEffects, Boolean pDuplication){
		setEventEffects(Event.class, pEffects, pDuplication);
	}
	
	/**
	 * Sets the Ack event probability value.
	 * 
	 * @param pProbability A probability value.
	 * @return Returns a value of a probability of the type Double which has been set.
	 */
	public static Double setAckEventProbability(Double pProbability){
		return setEventProbability(AckEvent.class, pProbability);
	}
	
	/**
	 * Sets the Ack event delay value.
	 * 
	 * @param pDelay A delay value.
	 * @return Returns a value of a delay of the type Long which has been set.
	 */
	public static Long setAckEventDelay(Long pDelay){
		return setEventDelay(AckEvent.class, pDelay);
	}
	
	/**
	 * Set the Ack event effects.
	 * 
	 * @param pEffects If set to <i>true</i> effects are enabled.
	 * @param pDuplication If set to <i>true</i> an event duplication is enabled. If set to <i>false</i> an event loss is enabled.
	 */
	public static void setAckEventEffects(Boolean pEffects, Boolean pDuplication){
		setEventEffects(AckEvent.class, pEffects, pDuplication);
	}
	
	/**
	 * Sets the AlarmInit event probability value.
	 * 
	 * @param pProbability A probability value.
	 * @return Returns a value of a probability of the type Double which has been set.
	 */
	public static Double setAlarmInitEventProbability(Double pProbability){
		return setEventProbability(AlarmInitEvent.class, pProbability);
	}
	
	/**
	 * Sets the AlarmInit event delay value.
	 * 
	 * @param pDelay A delay value.
	 * @return Returns a value of a delay of the type Long which has been set.
	 */
	public static Long setAlarmInitEventDelay(Long pDelay){
		return setEventDelay(AlarmInitEvent.class, pDelay);
	}
	
	/**
	 * Set the AlarmInit event effects.
	 * 
	 * @param pEffects If set to <i>true</i> effects are enabled.
	 * @param pDuplication If set to <i>true</i> an event duplication is enabled. If set to <i>false</i> an event loss is enabled.
	 */
	public static void setAlarmInitEventEffects(Boolean pEffects, Boolean pDuplication){
		setEventEffects(AlarmInitEvent.class, pEffects, pDuplication);
	}
	
	/**
	 * Sets the AppMessage event probability value.
	 * 
	 * @param pProbability A probability value.
	 * @return Returns a value of a probability of the type Double which has been set.
	 */
	public static Double setAppMessageEventProbability(Double pProbability){
		return setEventProbability(AppMessageEvent.class, pProbability);
	}
	
	/**
	 * Sets the AppMessage event delay value.
	 * 
	 * @param pDelay A delay value.
	 * @return Returns a value of a delay of the type Long which has been set.
	 */
	public static Long setAppMessageEventDelay(Long pDelay){
		return setEventDelay(AppMessageEvent.class, pDelay);
	}
	
	/**
	 * Set the AppMessage event effects.
	 * 
	 * @param pEffects If set to <i>true</i> effects are enabled.
	 * @param pDuplication If set to <i>true</i> an event duplication is enabled. If set to <i>false</i> an event loss is enabled.
	 */
	public static void setAppMessageEventEffects(Boolean pEffects, Boolean pDuplication){
		setEventEffects(AppMessageEvent.class, pEffects, pDuplication);
	}
	
	/**
	 * Sets the AppMessageReturn event probability value.
	 * 
	 * @param pProbability A probability value.
	 * @return Returns a value of a probability of the type Double which has been set.
	 */
	public static Double setAppMessageReturnEventProbability(Double pProbability){
		return setEventProbability(AppMessageReturnEvent.class, pProbability);
	}
	
	/**
	 * Sets the AppMessageReturn event delay value.
	 * 
	 * @param pDelay A delay value.
	 * @return Returns a value of a delay of the type Long which has been set.
	 */
	public static Long setAppMessageReturnEventDelay(Long pDelay){
		return setEventDelay(AppMessageReturnEvent.class, pDelay);
	}
	
	/**
	 * Set the AppMessageReturn event effects.
	 * 
	 * @param pEffects If set to <i>true</i> effects are enabled.
	 * @param pDuplication If set to <i>true</i> an event duplication is enabled. If set to <i>false</i> an event loss is enabled.
	 */
	public static void setAppMessageReturnEventEffects(Boolean pEffects, Boolean pDuplication){
		setEventEffects(AppMessageReturnEvent.class, pEffects, pDuplication);
	}
	
	/**
	 * Sets the Frame event probability value.
	 * 
	 * @param pProbability A probability value.
	 * @return Returns a value of a probability of the type Double which has been set.
	 */
	public static Double setFrameEventProbability(Double pProbability){
		return setEventProbability(FrameEvent.class, pProbability);
	}
	
	/**
	 * Sets the Frame event delay value.
	 * 
	 * @param pDelay A delay value.
	 * @return Returns a value of a delay of the type Long which has been set.
	 */
	public static Long setFrameEventDelay(Long pDelay){
		return setEventDelay(FrameEvent.class, pDelay);
	}
	
	/**
	 * Set the Frame event effects.
	 * 
	 * @param pEffects If set to <i>true</i> effects are enabled.
	 * @param pDuplication If set to <i>true</i> an event duplication is enabled. If set to <i>false</i> an event loss is enabled.
	 */
	public static void setFrameEventEffects(Boolean pEffects, Boolean pDuplication){
		setEventEffects(FrameEvent.class, pEffects, pDuplication);
	}
	
	/**
	 * Sets the TimerExpired event probability value.
	 * 
	 * @param pProbability A probability value.
	 * @return Returns a value of a probability of the type Double which has been set.
	 */
	public static Double setTimerExpiredEventProbability(Double pProbability){
		return setEventProbability(TimerExpiredEvent.class, pProbability);
	}
	
	/**
	 * Sets the TimerExpired event delay value.
	 * 
	 * @param pDelay A delay value.
	 * @return Returns a value of a delay of the type Long which has been set.
	 */
	public static Long setTimerExpiredEventDelay(Long pDelay){
		return setEventDelay(TimerExpiredEvent.class, pDelay);
	}
	
	/**
	 * Set the TimerExpired event effects.
	 * 
	 * @param pEffects If set to <i>true</i> effects are enabled.
	 * @param pDuplication If set to <i>true</i> an event duplication is enabled. If set to <i>false</i> an event loss is enabled.
	 */
	public static void setTimerExpiredEventEffects(Boolean pEffects, Boolean pDuplication){
		setEventEffects(TimerExpiredEvent.class, pEffects, pDuplication);
	}
	
	
	private static EventParameters getEventParameters(Class<? extends Event> pEventType){
		return eventParametersMap.get(pEventType);
	}

	private static EventParameters addEventParameters(Class<? extends Event> pEvent, EventParameters pEventParameters){
		return eventParametersMap.put(pEvent, pEventParameters);
	}
	
	
	private static Long setEventDelay(Class<? extends Event> pEvent, Long pDelay){
		EventParameters params;
		
		if(pDelay < 0){
			pDelay = 0L;
			DesktopFrame.writeLogError(NameConsts.INCORRECTDELAYSETTINGADEFAULTVALUEFOR + pEvent.getSimpleName());
		}
			
		
		if(eventParametersMap.containsKey(pEvent)){
			params = eventParametersMap.get(pEvent);
		} else {
			params = new EventParameters();
		}
		
		params.setDelay(pDelay);
		addEventParameters(pEvent, params);
		
		return pDelay;
	}
	
	private static Double setEventProbability(Class<? extends Event> pEvent, Double pProbability){
		EventParameters params;
		
		if(pProbability < 0 || pProbability > 1){
			pProbability = 1.0;
			DesktopFrame.writeLogError(NameConsts.INCORRECTPROBABILITYSETTINGADEFAULTVALUEFOR + pEvent.getSimpleName());
		}	
		
		if(eventParametersMap.containsKey(pEvent)){
			params = eventParametersMap.get(pEvent);
		} else {
			params = new EventParameters();
		}
		
		params.setProbability(pProbability);
		addEventParameters(pEvent, params);
		
		return pProbability;
	}
	
	private static void setEventEffects(Class<? extends Event> pEvent, Boolean pEffects, Boolean pDuplication){
		EventParameters params;
		
		if(eventParametersMap.containsKey(pEvent)){
			params = eventParametersMap.get(pEvent);
		} else {
			params = new EventParameters();
		}
		
		params.setEffects(pEffects);
		params.setDuplication(pDuplication);
		
		addEventParameters(pEvent, params);
	}
	
	/**
	 * Gets a delay value for a chosen event type.
	 * 
	 * @param pEvent A chosen event type.
	 * @return Returns an event's delay value of the type Long.
	 */
	public static Long getEventDelay(Class<? extends Event> pEvent){
		Class<? extends Event> event = useGlobalEventIfEnabled(pEvent);
		return getEventDelayByEventType(event);
	}
	
	private static Long getEventDelayByEventType(Class<? extends Event> pEvent){
		Long delay = 0L;
		
		if(eventParametersMap.containsKey(pEvent)){
			EventParameters params = getEventParameters(pEvent);
			
			if(params.getDelay() != null){
				delay = params.getDelay();
			}
		}
		
		return delay;
	}
	
	/**
	 * Gets a probability value for a chosen event type.
	 * 
	 * @param pEvent A chosen event type.
	 * @return Returns an event's probability value of the type Double.
	 */
	public static Double getEventProbability(Class<? extends Event> pEvent){
		Class<? extends Event> event = useGlobalEventIfEnabled(pEvent);
		return getEventProbabilityByEventType(event);
	}
	
	private static Double getEventProbabilityByEventType(Class<? extends Event> pEvent){
		Double probability = 1.0;
		
		if(eventParametersMap.containsKey(pEvent)){
			EventParameters params = getEventParameters(pEvent);
			
			if(params.getProbability() != null){
				probability = params.getProbability();
			}
		}
		
		return probability;
	}
	
	/**
	 * Checks if effects are enabled for a chosen event type.
	 * 
	 * @param pEvent A chosen event type.
	 * @return Returns <i>true</i> if effects are enabled. Otherwise <i>false</i> is returned.
	 */
	public static Boolean isEventEffectsEnabledAndReadyToUse(Class<? extends Event> pEvent){
		Boolean readyToUse = false;
		Class<? extends Event> event = useGlobalEventIfEnabled(pEvent);
		
		if(eventParametersMap.containsKey(event)){
			EventParameters params = getEventParameters(event);
			
			if(params.getEffects() != null){
				Boolean effectsEnabled = params.getEffects();
				
				if(effectsEnabled){
					Boolean effectsAlreadyUsed = params.getEffectAlreadyUsed();
					
					// reverse
					params.setEffectAlreadyUsed(!effectsAlreadyUsed);
					addEventParameters(event, params);
					
					readyToUse = !effectsAlreadyUsed;
				}
			}
		}
		
		return readyToUse;
	}
	
	/**
	 * Checks if the duplication is enabled for a chosen event type.
	 * 
	 * @param pEvent A chosen event type.
	 * @return Returns <i>true</i> if the duplication is enabled. Otherwise <i>false</i> is returned.
	 */
	public static Boolean isEventDuplicationEnabled(Class<? extends Event> pEvent){
		Boolean duplicationEnabled = false;
		Class<? extends Event> event = useGlobalEventIfEnabled(pEvent);
		
		if(eventParametersMap.containsKey(event)){
			EventParameters params = getEventParameters(event);
			
			if(params.getEffects() != null){
				duplicationEnabled = params.getDuplication();
			}
		}
		
		return duplicationEnabled;
	}
	
	private static Class<? extends Event> useGlobalEventIfEnabled(Class<? extends Event> pEvent){
		Class<? extends Event> event = pEvent;
		
		if(useSimplifiedValues){
			event = Event.class;
		}
		
		return event;
	}
}